package arithm

import (
	"math/big"
	"testing"
)

// TestIntegerAddWithInteger tests Integer.Add method with Integer values
func TestIntegerAddWithInteger(t *testing.T) {
	// Test normal addition
	x := Integer(5)
	y := Integer(3)
	result := x.ADD(y)
	expected := Integer(8)

	if result.Type() != expected.Type() {
		t.Errorf("Expected result type %v, got %v", expected.Type(), result.Type())
	}

	if result.String() != expected.String() {
		t.Errorf("Expected %v, got %v", expected, result)
	}

	// Test addition with negative numbers
	x = Integer(-5)
	y = Integer(3)
	result = x.ADD(y)
	expected = Integer(-2)

	if result.Type() != expected.Type() {
		t.Errorf("Expected result type %v, got %v", expected.Type(), result.Type())
	}

	if result.String() != expected.String() {
		t.Errorf("Expected %v, got %v", expected, result)
	}

	// Test addition with both negative numbers
	x = Integer(-5)
	y = Integer(-3)
	result = x.ADD(y)
	expected = Integer(-8)

	if result.Type() != expected.Type() {
		t.Errorf("Expected result type %v, got %v", expected.Type(), result.Type())
	}

	if result.String() != expected.String() {
		t.Errorf("Expected %v, got %v", expected, result)
	}
}

// TestIntegerAddOverflow tests Integer.Add method when overflow occurs
func TestIntegerAddOverflow(t *testing.T) {
	// Test positive overflow
	x := Integer(1<<63 - 1) // Large positive integer
	y := Integer(1)         // Another large positive integer
	result := x.ADD(y)

	// Result should be a BigInteger due to overflow
	if result.Type() != BIGINTEGER_TYPE {
		t.Errorf("Expected result type %v, got %v", BIGINTEGER_TYPE, result.Type())
	}

	// Check if the result is correct
	expectedBig := new(big.Int).Add(big.NewInt(1<<63-1), big.NewInt(1))
	if result.String() != expectedBig.String() {
		t.Errorf("Expected %v, got %v", expectedBig, result)
	}

	// Test negative overflow
	x = Integer(-1 << 63)   // Minimum int64 value
	y = Integer(-(1 << 62)) // Large negative value
	result = x.ADD(y)

	// Result should be a BigInteger due to overflow
	if result.Type() != BIGINTEGER_TYPE {
		t.Errorf("Expected result type %v, got %v", BIGINTEGER_TYPE, result.Type())
	}

	// Check if the result is correct
	expectedBig = new(big.Int).Add(big.NewInt(-1<<63), big.NewInt(-(1 << 62)))
	if result.String() != expectedBig.String() {
		t.Errorf("Expected %v, got %v", expectedBig, result)
	}
}

// TestIntegerAddWithBigInteger tests Integer.Add method with BigInteger values
func TestIntegerAddWithBigInteger(t *testing.T) {
	// Test addition with BigInteger
	x := Integer(5)
	bigVal, _ := (new(big.Int)).SetString("12345678901234567890", 10)
	y := (*BigInteger)(bigVal)
	result := x.ADD(y)

	// When adding Integer with BigInteger, it should delegate to BigInteger.ADD
	// which should return a BigInteger
	if result.Type() != BIGINTEGER_TYPE {
		t.Errorf("Expected result type %v, got %v", BIGINTEGER_TYPE, result.Type())
	}

	expectedBig := new(big.Int).Add(big.NewInt(5), bigVal)
	if result.String() != expectedBig.String() {
		t.Errorf("Expected %v, got %v", expectedBig, result)
	}
}

// TestIntegerAddWithRational tests Integer.Add method with Rational values
func TestIntegerAddWithRational(t *testing.T) {
	// Test addition with Rational
	x := Integer(5)
	ratVal := big.NewRat(7, 2) // 3.5
	y := (*Rational)(ratVal)
	result := x.ADD(y)

	// When adding Integer with Rational, it should delegate to Rational.ADD
	// which should return a Rational
	if result.Type() != RATIONAL_TYPE {
		t.Errorf("Expected result type %v, got %v", RATIONAL_TYPE, result.Type())
	}

	expectedRat := new(big.Rat).Add(big.NewRat(5, 1), ratVal)
	if result.String() != expectedRat.String() {
		t.Errorf("Expected %v, got %v", expectedRat, result)
	}
}

func TestIntegerAddWithReal(t *testing.T) {
	// Test addition with Real
	x := Integer(5)
	y := Real(3.14)
	result := x.ADD(y)

	// When adding Integer with Real, it should delegate to Real.ADD
	// which should return a Real
	if result.Type() != REAL_TYPE {
		t.Errorf("Expected result type %v, got %v", REAL_TYPE, result.Type())
	}

	expected := Real(8.14)
	if result.String() != expected.String() {
		t.Errorf("Expected %v, got %v", expected, result)
	}
}

// TestIntegerAddWithComplex tests Integer.Add method with Complex values
func TestIntegerAddWithComplex(t *testing.T) {
	// Test addition with Complex
	y := Integer(5)
	x := Complex(3 + 2i)
	result := x.ADD(y)

	// When adding Integer with Complex, it should delegate to Complex.ADD
	// which should return a Complex
	if result.Type() != COMPLEX_TYPE {
		t.Errorf("Expected result type %v, got %v", COMPLEX_TYPE, result.Type())
	}

	expected := Complex(8 + 2i)
	if result.String() != expected.String() {
		t.Errorf("Expected %v, got %v", expected, result)
	}
}

// TestIntegerSubWithInteger tests Integer.SUB method with Integer values
func TestIntegerSubWithInteger(t *testing.T) {
	// Test normal subtraction
	x := Integer(5)
	y := Integer(3)
	result := x.SUB(y)
	expected := Integer(2)

	if result.Type() != expected.Type() {
		t.Errorf("Expected result type %v, got %v", expected.Type(), result.Type())
	}

	if result.String() != expected.String() {
		t.Errorf("Expected %v, got %v", expected, result)
	}

	// Test subtraction with negative numbers
	x = Integer(-5)
	y = Integer(3)
	result = x.SUB(y)
	expected = Integer(-8)

	if result.Type() != expected.Type() {
		t.Errorf("Expected result type %v, got %v", expected.Type(), result.Type())
	}

	if result.String() != expected.String() {
		t.Errorf("Expected %v, got %v", expected, result)
	}

	// Test subtraction with both negative numbers
	x = Integer(-5)
	y = Integer(-3)
	result = x.SUB(y)
	expected = Integer(-2)

	if result.Type() != expected.Type() {
		t.Errorf("Expected result type %v, got %v", expected.Type(), result.Type())
	}

	if result.String() != expected.String() {
		t.Errorf("Expected %v, got %v", expected, result)
	}
}

// TestIntegerSubWithBigInteger tests Integer.SUB method with BigInteger values
func TestIntegerSubWithBigInteger(t *testing.T) {
	// Test subtraction with BigInteger
	x := Integer(5)
	bigVal, _ := (new(big.Int)).SetString("12345678901234567890", 10)
	y := (*BigInteger)(bigVal)
	result := x.SUB(y)

	// When subtracting Integer with BigInteger, it should delegate to BigInteger.ADD with negated value
	// which should return a BigInteger
	if result.Type() != BIGINTEGER_TYPE {
		t.Errorf("Expected result type %v, got %v", BIGINTEGER_TYPE, result.Type())
	}

	expectedBig := new(big.Int).Sub(big.NewInt(5), bigVal)
	if result.String() != expectedBig.String() {
		t.Errorf("Expected %v, got %v", expectedBig, result)
	}
}

// TestIntegerSubWithRational tests Integer.SUB method with Rational values
func TestIntegerSubWithRational(t *testing.T) {
	// Test subtraction with Rational
	x := Integer(5)
	ratVal := big.NewRat(7, 2) // 3.5
	y := (*Rational)(ratVal)
	result := x.SUB(y)

	// When subtracting Integer with Rational, it should delegate to Rational.ADD with negated value
	// which should return a Rational
	if result.Type() != RATIONAL_TYPE {
		t.Errorf("Expected result type %v, got %v", RATIONAL_TYPE, result.Type())
	}

	expectedRat := new(big.Rat).Sub(big.NewRat(5, 1), ratVal)
	if result.String() != expectedRat.String() {
		t.Errorf("Expected %v, got %v", expectedRat, result)
	}
}

// TestIntegerSubWithReal tests Integer.SUB method with Real values
func TestIntegerSubWithReal(t *testing.T) {
	// Test subtraction with Real
	x := Integer(5)
	y := Real(3.14)
	result := x.SUB(y)

	// When subtracting Integer with Real, it should delegate to Real.ADD with negated value
	// which should return a Real
	if result.Type() != REAL_TYPE {
		t.Errorf("Expected result type %v, got %v", REAL_TYPE, result.Type())
	}

	expected := Real(1.86)
	if result.String() != expected.String() {
		t.Errorf("Expected %v, got %v", expected, result)
	}
}

// TestIntegerSubWithComplex tests Integer.SUB method with Complex values
func TestIntegerSubWithComplex(t *testing.T) {
	// Test subtraction with Complex
	x := Integer(5)
	y := Complex(3 + 2i)
	result := x.SUB(y)

	// When subtracting Integer with Complex, it should delegate to Complex.ADD with negated value
	// which should return a Complex
	if result.Type() != COMPLEX_TYPE {
		t.Errorf("Expected result type %v, got %v", COMPLEX_TYPE, result.Type())
	}

	expected := Complex(2 - 2i)
	if result.String() != expected.String() {
		t.Errorf("Expected %v, got %v", expected, result)
	}
}

// TestIntegerMulWithInteger tests Integer.MUL method with Integer values
func TestIntegerMulWithInteger(t *testing.T) {
	// Test normal multiplication
	x := Integer(5)
	y := Integer(3)
	result := x.MUL(y)
	expected := Integer(15)

	if result.Type() != expected.Type() {
		t.Errorf("Expected result type %v, got %v", expected.Type(), result.Type())
	}

	if result.String() != expected.String() {
		t.Errorf("Expected %v, got %v", expected, result)
	}

	// Test multiplication with negative numbers
	x = Integer(-5)
	y = Integer(3)
	result = x.MUL(y)
	expected = Integer(-15)

	if result.Type() != expected.Type() {
		t.Errorf("Expected result type %v, got %v", expected.Type(), result.Type())
	}

	if result.String() != expected.String() {
		t.Errorf("Expected %v, got %v", expected, result)
	}

	// Test multiplication with both negative numbers
	x = Integer(-5)
	y = Integer(-3)
	result = x.MUL(y)
	expected = Integer(15)

	if result.Type() != expected.Type() {
		t.Errorf("Expected result type %v, got %v", expected.Type(), result.Type())
	}

	if result.String() != expected.String() {
		t.Errorf("Expected %v, got %v", expected, result)
	}

	// Test multiplication by zero
	x = Integer(5)
	y = Integer(0)
	result = x.MUL(y)
	expected = Integer(0)

	if result.Type() != expected.Type() {
		t.Errorf("Expected result type %v, got %v", expected.Type(), result.Type())
	}

	if result.String() != expected.String() {
		t.Errorf("Expected %v, got %v", expected, result)
	}
}

// TestIntegerMulOverflow tests Integer.MUL method when overflow occurs
func TestIntegerMulOverflow(t *testing.T) {
	// Test positive overflow - values that when multiplied exceed int64 max
	x := Integer(1 << 62) // Large positive integer
	y := Integer(4)       // Another value that will cause overflow when multiplied
	result := x.MUL(y)

	// Result should be a BigInteger due to overflow
	if result.Type() != BIGINTEGER_TYPE {
		t.Errorf("Expected result type %v, got %v", BIGINTEGER_TYPE, result.Type())
	}

	// Check if the result is correct
	expectedBig := new(big.Int).Mul(big.NewInt(1<<62), big.NewInt(4))
	if result.String() != expectedBig.String() {
		t.Errorf("Expected %v, got %v", expectedBig, result)
	}

	// Test negative overflow
	x = Integer(1 << 62) // Large positive integer
	y = Integer(-4)      // Negative value that will cause overflow when multiplied
	result = x.MUL(y)

	// Result should be a BigInteger due to overflow
	if result.Type() != BIGINTEGER_TYPE {
		t.Errorf("Expected result type %v, got %v", BIGINTEGER_TYPE, result.Type())
	}

	// Check if the result is correct
	expectedBig = new(big.Int).Mul(big.NewInt(1<<62), big.NewInt(-4))
	if result.String() != expectedBig.String() {
		t.Errorf("Expected %v, got %v", expectedBig, result)
	}
}

// TestIntegerMulWithBigInteger tests Integer.MUL method with BigInteger values
func TestIntegerMulWithBigInteger(t *testing.T) {
	// Test multiplication with BigInteger
	x := Integer(5)
	bigVal, _ := (new(big.Int)).SetString("12345678901234567890", 10)
	y := (*BigInteger)(bigVal)
	result := x.MUL(y)

	// When multiplying Integer with BigInteger, it should delegate to BigInteger.MUL
	// which should return a BigInteger
	if result.Type() != BIGINTEGER_TYPE {
		t.Errorf("Expected result type %v, got %v", BIGINTEGER_TYPE, result.Type())
	}

	expectedBig := new(big.Int).Mul(big.NewInt(5), bigVal)
	if result.String() != expectedBig.String() {
		t.Errorf("Expected %v, got %v", expectedBig, result)
	}
}

// TestIntegerMulWithRational tests Integer.MUL method with Rational values
func TestIntegerMulWithRational(t *testing.T) {
	// Test multiplication with Rational
	x := Integer(5)
	ratVal := big.NewRat(7, 2) // 3.5
	y := (*Rational)(ratVal)
	result := x.MUL(y)

	// When multiplying Integer with Rational, it should delegate to Rational.MUL
	// which should return a Rational
	if result.Type() != RATIONAL_TYPE {
		t.Errorf("Expected result type %v, got %v", RATIONAL_TYPE, result.Type())
	}

	expectedRat := new(big.Rat).Mul(big.NewRat(5, 1), ratVal)
	if result.String() != expectedRat.String() {
		t.Errorf("Expected %v, got %v", expectedRat, result)
	}
}

// TestIntegerMulWithReal tests Integer.MUL method with Real values
func TestIntegerMulWithReal(t *testing.T) {
	// Test multiplication with Real
	x := Integer(5)
	y := Real(3.14)
	result := x.MUL(y)

	// When multiplying Integer with Real, it should delegate to Real.MUL
	// which should return a Real
	if result.Type() != REAL_TYPE {
		t.Errorf("Expected result type %v, got %v", REAL_TYPE, result.Type())
	}

	expected := Real(15.7) // 5 * 3.14
	if result.String() != expected.String() {
		t.Errorf("Expected %v, got %v", expected, result)
	}
}

// TestIntegerMulWithComplex tests Integer.MUL method with Complex values
func TestIntegerMulWithComplex(t *testing.T) {
	// Test multiplication with Complex
	x := Integer(5)
	y := Complex(3 + 2i)
	result := x.MUL(y)

	// When multiplying Integer with Complex, it should delegate to Complex.MUL
	// which should return a Complex
	if result.Type() != COMPLEX_TYPE {
		t.Errorf("Expected result type %v, got %v", COMPLEX_TYPE, result.Type())
	}

	expected := Complex(15 + 10i) // 5 * (3 + 2i)
	if result.String() != expected.String() {
		t.Errorf("Expected %v, got %v", expected, result)
	}
}

// TestIntegerDivWithInteger tests Integer.DIV method with Integer values
func TestIntegerDivWithInteger(t *testing.T) {
	// Test normal division that results in an integer
	x := Integer(6)
	y := Integer(3)
	result := x.DIV(y)

	// 6 / 3 = 2, which should be an Integer
	if result.Type() != INTEGER_TYPE {
		t.Errorf("Expected result type %v, got %v", INTEGER_TYPE, result.Type())
	}

	expected := Integer(2)
	if result.String() != expected.String() {
		t.Errorf("Expected %v, got %v", expected, result)
	}

	// Test division that results in a rational
	x = Integer(5)
	y = Integer(2)
	result = x.DIV(y)

	// 5 / 2 = 5/2, which should be a Rational
	if result.Type() != RATIONAL_TYPE {
		t.Errorf("Expected result type %v, got %v", RATIONAL_TYPE, result.Type())
	}

	expectedRat := big.NewRat(5, 2)
	if result.String() != expectedRat.String() {
		t.Errorf("Expected %v, got %v", expectedRat, result)
	}

	// Test division with negative numbers
	x = Integer(-6)
	y = Integer(3)
	result = x.DIV(y)

	if result.Type() != INTEGER_TYPE {
		t.Errorf("Expected result type %v, got %v", INTEGER_TYPE, result.Type())
	}

	expected = Integer(-2)
	if result.String() != expected.String() {
		t.Errorf("Expected %v, got %v", expected, result)
	}

	// Test division with both negative numbers
	x = Integer(-6)
	y = Integer(-3)
	result = x.DIV(y)

	if result.Type() != INTEGER_TYPE {
		t.Errorf("Expected result type %v, got %v", INTEGER_TYPE, result.Type())
	}

	expected = Integer(2)
	if result.String() != expected.String() {
		t.Errorf("Expected %v, got %v", expected, result)
	}
}

// TestIntegerDivWithBigInteger tests Integer.DIV method with BigInteger values
func TestIntegerDivWithBigInteger(t *testing.T) {
	// Test division with BigInteger
	x := Integer(5)
	bigVal, _ := (new(big.Int)).SetString("12345678901234567890", 10)
	y := (*BigInteger)(bigVal)
	result := x.DIV(y)

	// When dividing Integer by BigInteger, it should return a Rational
	if result.Type() != RATIONAL_TYPE {
		t.Errorf("Expected result type %v, got %v", RATIONAL_TYPE, result.Type())
	}

	expectedRat := new(big.Rat).SetFrac(big.NewInt(5), bigVal)
	if result.String() != expectedRat.String() {
		t.Errorf("Expected %v, got %v", expectedRat, result)
	}
}

// TestIntegerDivWithRational tests Integer.DIV method with Rational values
func TestIntegerDivWithRational(t *testing.T) {
	// Test division with Rational
	x := Integer(5)
	ratVal := big.NewRat(7, 2) // 3.5
	y := (*Rational)(ratVal)
	result := x.DIV(y)

	// When dividing Integer by Rational, it should return a Rational
	if result.Type() != RATIONAL_TYPE {
		t.Errorf("Expected result type %v, got %v", RATIONAL_TYPE, result.Type())
	}

	// 5 / (7/2) = 5 * 2/7 = 10/7
	expectedRat := big.NewRat(10, 7)
	if result.String() != expectedRat.String() {
		t.Errorf("Expected %v, got %v", expectedRat, result)
	}
}

// TestIntegerDivWithReal tests Integer.DIV method with Real values
func TestIntegerDivWithReal(t *testing.T) {
	// Test division with Real
	x := Integer(5)
	y := Real(2.5)
	result := x.DIV(y)

	// When dividing Integer by Real, it should return a Real
	if result.Type() != REAL_TYPE {
		t.Errorf("Expected result type %v, got %v", REAL_TYPE, result.Type())
	}

	expected := Real(2.0) // 5 / 2.5 = 2
	if result.String() != expected.String() {
		t.Errorf("Expected %v, got %v", expected, result)
	}
}

// TestIntegerDivWithComplex tests Integer.DIV method with Complex values
func TestIntegerDivWithComplex(t *testing.T) {
	// Test division with Complex
	x := Integer(10)
	y := Complex(2 + 1i)
	result := x.DIV(y)

	// When dividing Integer by Complex, it should return a Complex
	if result.Type() != COMPLEX_TYPE {
		t.Errorf("Expected result type %v, got %v", COMPLEX_TYPE, result.Type())
	}

	// 10 / (2 + i) = 10 * (2 - i) / ((2 + i) * (2 - i)) = 10 * (2 - i) / 5 = 2 * (2 - i) = 4 - 2i
	expected := Complex(4 - 2i)
	if result.String() != expected.String() {
		t.Errorf("Expected %v, got %v", expected, result)
	}
}
